/*
 *   Copyright (c) 2011 Hitachi Data Systems, Inc.
 *
 *   Permission is hereby granted to  this software and associated
 *   documentation files (the "Software"), subject to the terms and
 *   conditions of the Sample Source Code License (SSCL) delivered
 *   with this Software. If you do not agree to the terms and
 *   conditions of the SSCL,
 *
 *     (i)  you must close this file and delete all copies of the
 *          Software, and
 *     (ii) any permission to use the Software is expressly denied.
 *
 */

package com.hds.hcp.examples;


/***
 * This is a sample for how to utilize the HTTP Whole I/O feature of HCP version 4.1. 
 * 
 * The feature allows applications to send (PUT) and receive (GET) over HTTP that
 * includes both the object data and the custom metadata in one HTTP transmission
 * to save on the number of network transactions.
 * 
 * When the HCP system receives (PUT) content that is indicated as whole-object, it will
 * break apart the stream into the object data and custom-metadata parts and store them
 * appropriately.
 *
 * When the HCP system is requested to send (GET) content as whole-object, HCP will send
 * both the object data and the custom metadata together in the stream. The HTTP headers
 * returned as part of the response indicates what order the content is in and the size
 * of the object data.
 * 
 * Disclaimer: This code is only a sample and is provided for educational purposes.  The consumer
 *  of this sample assumes full responsibility for any effects due to coding errors.
 */

import java.io.FileInputStream;
import java.io.SequenceInputStream;

import java.io.FileOutputStream;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.util.EntityUtils;

import com.hds.hcp.apihelpers.HCPUtils;
import com.hds.hcp.apihelpers.WholeIOOutputStream;

public class WholeIO {

	// Static strings for the file path info.
	private static final String sBaseFileName = "wholeio.txt";
	
	// Local member variables.
	private Boolean bIsInitialized = false;
	private String sEncodedUserName, sEncodedPassword;
	private String sHCPURLFilePath;
	
	private HttpClient mHttpClient;

	/**
	 * Initialize the object by setting up internal data and establishing the HTTP client connection.
	 * 
	 * This routine is called by the ReadFromHCP and WriteToHCP routines, so calling it by the
	 * consumer of this class is unnecessary.
	 *
	 */
	void initialize(String inNamespace, String inUsername, String inPassword) throws Exception {

		if (! bIsInitialized)  // Only initialize if we haven't already
		{
			// Build the URL File Path on HCP.
			sHCPURLFilePath = "https://" + inNamespace + "/rest/examples/" + sBaseFileName;
			
			// Encode both username and password for authentication string.
			sEncodedUserName = HCPUtils.toBase64Encoding(inUsername);
			sEncodedPassword = HCPUtils.toMD5Digest(inPassword);
			
			mHttpClient = HCPUtils.initHttpClient();
			
			bIsInitialized = true;
		}
	}
	
	/**
	 * This method performs an orderly shutdown of HTTP connection manager.
	 * 
	 */
	void shutdown() throws Exception {
		// Clean up open connections by shutting down connection manager.
		mHttpClient.getConnectionManager().shutdown();
	}
	
	/**
	 * This method performs a PUT of an object data file and custom metadata
	 * file using the Whole I/O feature.
	 */
	void WriteToHCP() throws Exception {
		/*
		 * Setup the PUT request specifying it is whole-object I/O
		 */
		HttpPut httpRequest = new HttpPut(sHCPURLFilePath + "?type=whole-object");

		// Construct the Whole I/O Sequenced Stream
		FileInputStream dataFileStream = new FileInputStream(sBaseFileName);
			
		FileInputStream customMetadataStream = new FileInputStream(sBaseFileName + ".cm");

		SequenceInputStream wholeIOStream = new SequenceInputStream(
				dataFileStream, customMetadataStream);
		
		// Point the HttpRequest to the input stream.
		httpRequest.setEntity(new InputStreamEntity(wholeIOStream, -1));

		// Put the size of the data portion of the whole object into the X-HCP-Size header value.
		httpRequest.setHeader("X-HCP-Size", String.valueOf(dataFileStream.available()));

		// Set HTTP Authorization Header with HCP credentials.
		httpRequest.setHeader(HCPUtils.HTTP_AUTH_HEADER, "HCP " + sEncodedUserName + ":" + sEncodedPassword);

		/*
		 * Now execute the PUT request.
		 */
		HttpResponse httpResponse = mHttpClient.execute(httpRequest);
	        
		// For debugging purposes, dump out the HTTP Response.
		HCPUtils.dumpHttpResponse(httpResponse);
	        
		// If the return code is anything BUT 200 range indicating success, we have to throw an exception.
		if (2 != (int)(httpResponse.getStatusLine().getStatusCode() / 100))
		{
			// Clean up after ourselves and release the HTTP connection to the connection manager.
			EntityUtils.consume(httpResponse.getEntity());
			
			wholeIOStream.close();
			dataFileStream.close();
			customMetadataStream.close();
			
			throw new HttpResponseException(httpResponse.getStatusLine().getStatusCode(),
					"Unexpected status returned from " + httpRequest.getMethod() + " ("
					+ httpResponse.getStatusLine().getStatusCode() + ": " 
					+ httpResponse.getStatusLine().getReasonPhrase() + ")");
		}

		// Clean up after ourselves and release the HTTP connection to the connection manager.
		EntityUtils.consume(httpResponse.getEntity());
		
		wholeIOStream.close();
		dataFileStream.close();
		customMetadataStream.close();
	}
	
	/**
	 * This method performs a GET of a file and its custom metadata content utilizing 
	 * the Whole I/O feature.
	 */
	void ReadFromHCP() throws Exception {
		/*
		 * Setup the GET request and specifying the whole-object I/O.
		 */
		HttpGet httpRequest = new HttpGet(sHCPURLFilePath + "?type=whole-object");
	
		// Set HTTP Authorization Header with HCP credentials.
		httpRequest.setHeader(HCPUtils.HTTP_AUTH_HEADER, "HCP " + sEncodedUserName + ":" + sEncodedPassword);

		// For demonstration purposes, request the custom metadata before the
		//   object data. Useful, if the application wishes to examine the
		//   custom metadata to set context for the data that will follow.
		httpRequest.setHeader("X-HCP-CustomMetadataFirst", "true");
		
		/*
		 * Now execute the GET request.
		 */
		HttpResponse httpResponse = mHttpClient.execute(httpRequest);
	        
		// For debugging purposes, dump out the HTTP Response.
		HCPUtils.dumpHttpResponse(httpResponse);
	        
		// If the return code is anything BUT 200 range indicating success, we have to throw an exception.
		if (2 != (int)(httpResponse.getStatusLine().getStatusCode() / 100))
		{
			// Clean up after ourselves and release the HTTP connection to the connection manager.
			EntityUtils.consume(httpResponse.getEntity());
			
			throw new HttpResponseException(httpResponse.getStatusLine().getStatusCode(),
					"Unexpected status returned from " + httpRequest.getMethod() + " ("
					+ httpResponse.getStatusLine().getStatusCode() + ": " 
					+ httpResponse.getStatusLine().getReasonPhrase() + ")");
	    }

		/* 
		 * Determine if data or custom metadata is first.
		 */
		Boolean cmFirst = new Boolean(httpResponse.getFirstHeader("X-HCP-CustomMetadataFirst").getValue());

		/*
		 * Determine the size of the first part based on whether data or custom metadata is first.
		 */
		
		// Assume object data is first.
		int firstPartSize = Integer.valueOf(httpResponse.getFirstHeader("X-HCP-Size").getValue());

		// If custom metadata is first, do the math.
		if (cmFirst) {
			// subtract the data size from the content length returned.
			firstPartSize = Integer.valueOf(httpResponse.getFirstHeader("Content-Length").getValue())
			                - firstPartSize;
		}

		/*
		 * Split and write the file to disk.
		 */
		WholeIOOutputStream outputCreator
	        = new WholeIOOutputStream(
	        		new FileOutputStream(sBaseFileName + ".fromHCP"), 
	        		new FileOutputStream(sBaseFileName + ".fromHCP.cm"),
	        		cmFirst);
	        
	    outputCreator.copy(httpResponse.getEntity().getContent(), firstPartSize);
	        
	    outputCreator.close();  // Files should be created.

	    // Clean up after ourselves and release the HTTP connection to the connection manager.
		EntityUtils.consume(httpResponse.getEntity());
	}
	
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {

		WholeIO myClass = new WholeIO();

		if (args.length != 3) {
			System.out.println();
			System.out.println("Usage: " + myClass.getClass().getSimpleName() + " <DNS-Namespace> <Username> <Password>\n");
			System.out.println("  where <DNS-Namespace> is the fully qualified DNS name of the HCP Namespace.");
			System.out.println("              For example: \"ns1.ten1.myhcp.example.com\"");
			System.out.println("        <Username> and <Password> are the credentials of the HCP user with data");
			System.out.println("              access permissions for the namespace.");
			System.out.println();
			
			System.exit(-1);
		}
		
		try {
			// Initialize the class with the input parameters.
			myClass.initialize(args[0], args[1], args[2]);
			
			// Write Object to HCP
			myClass.WriteToHCP();

			// Read Object back from HCP
			myClass.ReadFromHCP();
			
			// Cleanup before object destruction.
			myClass.shutdown();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
